﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using PodpisyElektorniczne.Interfaces;
using BI = System.Numerics.BigInteger;

namespace PodpisyElektorniczne.Classes
{
    class Seqdl : ISeqdl
    {
        public List<BI> VectorC { get; set; }
        public List<BI> VectorS { get; set; }

        public Seqdl()
        {
            VectorC = new List<BI>();
            VectorS = new List<BI>();
        }

        public override string ToString()
        {
            var result = VectorC.Aggregate(string.Empty, (current, bi) => current + bi.ToString());
            result += VectorS.Aggregate(string.Empty, (current, bi) => current + bi.ToString());

            return result;
        }

        public void SeqdlSignature(SchemaSetts schemaSetts, UserKeys key,KeyPreferences kSetts, BI dashedYw, List<BI> pubKeys, List<BI> saltW, BI msg)
        {
            var t = new List<BI>();
            var u = new List<BI>();

            var rng = new RNGCryptoServiceProvider();
            var rngBytes = new byte[kSetts.LengthOrderZq];
            rng.GetBytes(rngBytes);
            if (saltW.Count > 1)
            {
                for (var i = 0; i < saltW.Count - 1; i++)
                {
                    rng.GetBytes(rngBytes);
                    var cI = new System.Numerics.BigInteger(rngBytes);
                    VectorC.Add((cI < 0 ? cI*(-1) : cI) % schemaSetts.SubOrderQ);

                    rng = new RNGCryptoServiceProvider();

                    rng.GetBytes(rngBytes);
                    var sI = new System.Numerics.BigInteger(rngBytes);
                    VectorS.Add((sI < 0 ? sI * (-1) : sI) % schemaSetts.SubOrderQ);

                    rng = new RNGCryptoServiceProvider();

                    t.Add((BI.ModPow(schemaSetts.DashedGeneratorG, VectorS[i], schemaSetts.SubOrderQ)*
                           BI.ModPow(dashedYw, VectorC[i], schemaSetts.SubOrderQ))%schemaSetts.SubOrderQ);
                    u.Add((BI.ModPow(schemaSetts.GeneratorG, VectorS[i], schemaSetts.SubOrderQ)*
                           BI.ModPow(pubKeys[i]*saltW[i], VectorC[i], schemaSetts.SubOrderQ))%schemaSetts.SubOrderQ);
                }
            }
            t.Add(BI.ModPow(schemaSetts.DashedGeneratorG, schemaSetts.SecretR % schemaSetts.SubOrderQ, schemaSetts.SubOrderQ));
            u.Add(BI.ModPow(schemaSetts.GeneratorG, schemaSetts.SecretR % schemaSetts.SubOrderQ, schemaSetts.SubOrderQ));
            
            var toHash = string.Empty;
            toHash += schemaSetts.DashedGeneratorG.ToString() + schemaSetts.GeneratorG.ToString() +dashedYw.ToString();

            toHash = pubKeys.Aggregate(toHash, (current, p) => current.ToString() + p.ToString()); // czy tu ma być plus ? czy ma być agregate
            toHash = saltW.Aggregate(toHash, (current, p) => current.ToString() + p.ToString());

            for (var i = 0; i < t.Count; i++)
                toHash += string.Format("{0}{1}", t[i], u[i]);

            toHash += msg.ToString();

            var sumVectorC = VectorC.Aggregate<BI, BI>(0, (current, p) => current + p);

            var source = Encoding.ASCII.GetBytes(toHash);
            var md5Hash = new MD5CryptoServiceProvider().ComputeHash(source);

            VectorC.Add(BI.Abs((new BI(md5Hash)) - sumVectorC) % schemaSetts.SubOrderQ);
            //var tmp = (schemaSetts.SecretR - ((key.GetPrivateKey() + schemaSetts.SecretRj) * VectorC.Last())) % schemaSetts.SubOrderQ;
           // VectorS.Add(tmp < 0 ? tmp+schemaSetts.SubOrderQ : tmp);
            VectorS.Add((schemaSetts.SecretR - ((key.GetPrivateKey() + schemaSetts.SecretRj) * VectorC.Last()) % schemaSetts.SubOrderQ));

        }

        public bool VeryfieUj(BI rj, BI priv, BI gen, BI uj, BI mod, BI tj, BI dgen)
        {
            var computedR = VectorS.Last() + ((rj + priv)*VectorC.Last())%mod;
            computedR = computedR < 0 ? computedR + mod : computedR;
            if (computedR > 0)
            {
                var resUj = BI.ModPow(gen, computedR, mod);
                var resTj = BI.ModPow(dgen, computedR, mod);
                return (uj == resUj) & (tj == resTj);
            }
            return false;
        }

        

        public BI Pow(BI x, BI exp)
        {
            var intExp=0;
            if(Int32.TryParse(exp.ToString(),out intExp))
            {
                return BI.Pow(x, intExp);
            }
            else
            {
                var rest = Int32.Parse((exp%Int32.MaxValue).ToString());
                var result = BI.Pow(x, rest);
                var loopLength = BI.Divide((exp-rest),Int32.MaxValue);

                for (var i=0; i< loopLength;i++)
                {
                    result *= BI.Pow(x, Int32.MaxValue);
                }
                return result;
            }

        }
    }
}
